from domain.tactics import Tactics

from math import sqrt, pi, atan2
from random import random


class MatchTeam(object):
    
    def __init__(self, team, home):
        self.team = team
        self.tactics = Tactics()
        self.match_players = []
        for player_entry, pos in zip(team.players, self.tactics.formation.pos_list):
            n, p = player_entry
            mp = MatchPlayer(self, n, p, pos)
            self.match_players.append(mp)
        self.score = 0
        self.home = home
        
    def reposition(self):
        for mp in self.match_players:
            mp.reposition()

    def score_goal(self):
        self.score += 1


CARD_NONE = 0
CARD_YELLOW = 1
CARD_TWO_YELLOW = 2
CARD_RED = 3


class MatchPlayer(object):
    
    def __init__(self, match_team, number, player, tactical_position):
        self.match_team = match_team
        self.player = player
        self.number = number
        self.card = CARD_NONE
        self.goals = 0
        self.tactical_position = tactical_position
        self.position = None
        self.kick_cooldown = 0

    def __repr__(self):
        return self.player.name

    def reposition(self):
        real_pos = self.get_tactical_position_in_reality()
        self.position = [real_pos[0], 0, real_pos[1]]

    def show_card(self, card):
        if self.is_sent_off():
            raise ValueError('Player %s was shown a card but he has already '
                             'been sent off.')
        if card == CARD_YELLOW:
            if self.card == CARD_YELLOW:
                self.card = CARD_TWO_YELLOW
            else:
                self.card = CARD_YELLOW
        elif card == CARD_RED:
            self.card = CARD_RED
        else:
            raise ValueError('Either CARD_YELLOW or CARD_RED should be passed '
                             'to MatchPlayer.show_card()')

    def score_goal(self):
        self.goals += 1

    def is_sent_off(self):
        return self.card in (CARD_TWO_YELLOW, CARD_RED)

    def step(self, ball):
        if self.is_sent_off():
            return
        
        if self.kick_cooldown > 0:
            self.kick_cooldown -= 1
            return
        
        if ball.controller is self:
            teammate, angle = self.get_nearest_teammate_fwd()
            if teammate is None:
                self.shoot_at_goal(ball)
                #self.approach_xz_pos(self.get_target_goal_xz())
            else:
                if random() < 0.0:
                    self.random_kick(ball)
                else:
                    self.pass_ball(ball, angle)
        else:
            dist_to_ball = self.xz_distance_to(ball)
            if dist_to_ball <= 0.01:
                _ = ball.take_possession(self)
            elif dist_to_ball < 0.2:
                self.approach_xz_pos((ball.get_xz_position()))
            else:
                self.approach_xz_pos(self.get_tactical_position_in_reality())

    def xz_distance_to(self, body):
        return sqrt(((self.position[0] - body.position[0]) ** 2
                     + (self.position[2] - body.position[2]) ** 2))

    def approach_xz_pos(self, xz): 
        if self.position[0] < xz[0]:
            self.position[0] += 0.001
        elif self.position[0] > xz[0]:
            self.position[0] -= 0.001
            
        if self.position[2] < xz[1]:
            self.position[2] += 0.001
        elif self.position[2] > xz[1]:
            self.position[2] -= 0.001
            
    def get_nearest_teammate_fwd(self):
        best = None
        best_dist = 0
        for t in self.match_team.match_players:
            if t is not self and not t.is_sent_off():
                if self.match_team.home:
                    in_front = t.position[2] > self.position[2]
                else:
                    in_front = t.position[2] < self.position[2]
                dist = self.xz_distance_to(t)
                if in_front and (best is None or best_dist > dist):
                    best = t
                    best_dist = dist
        if best is None:
            return None, None
        else:
            return best, self.get_angle_to_body(best)

    def pass_ball(self, ball, angle):
        print '%s passes' % self
        ball.kick(kicker=self,
                  power=0.01,
                  angle=angle,
                  y_angle=0)
        self.kick_cooldown = 5
        
    def random_kick(self, ball):
        print '%s kicks randomly' % self
        ball.kick(kicker=self,
                  power=random() * 0.02,
                  angle=random() * 2 * pi,
                  y_angle=0)
        self.kick_cooldown = 5

    def shoot_at_goal(self, ball):
        angle = self.get_angle_to_opponent_goal()
        print '%s shoots!' % self
        ball.kick(kicker=self,
                  power=0.02,
                  angle=angle,
                  y_angle=0)
        self.kick_cooldown = 5

    def get_tactical_position_in_reality(self):
        if self.match_team.home:
            return self.tactical_position
        else:
            return (1 - self.tactical_position[0],
                    1 - self.tactical_position[1])

    def get_target_goal_xz(self):
        return (0.5, 0) if self.match_team.home else (0.5, 1)

    def get_angle_to_body(self, body):
        return atan2(body.position[2] - self.position[2],
                     body.position[0] - self.position[0])

    def get_angle_to_opponent_goal(self):
        if self.match_team.home:
            return atan2(1 - self.position[2],
                         0.5 - self.position[0])
        else:
            return atan2(0 - self.position[2],
                         0.5 - self.position[0])
